#include <stdio.h>
#include <stdarg.h>
// #include <sys/types.h>
#include <unistd.h>
// #include <sys/time.h>
#include <string.h>
// #include <pthread.h>
#include <stdint.h>
#include <stdlib.h>

#include "../log.h"

#define LOG_BUFFER_QUEUE_LEN 128
#define LOG_BUFFER_SIZE (0x1 << 14)

struct cc_log
{
    FILE* fp;
    // pthread_mutex_t lock;
    struct lock* _lock;
    char file[128];
    int delay_write;
    uint8_t buffer[LOG_BUFFER_QUEUE_LEN][LOG_BUFFER_SIZE];
    int buf_inused;
};

static inline void cc_log_lock(struct cc_log* l)
{
    // pthread_mutex_lock(&l->lock);
    if (l->_lock && l->_lock->lock_fp)
    {
        if (0 != l->_lock->lock_fp(l->_lock))
        {
            fprintf(stderr, "cc_log_lock(): lock failed.\n");
        }
    }
}

static inline void cc_log_unlock(struct cc_log* l)
{
    if (l->_lock && l->_lock->unlock_fp)
    {
        if (0 !=l->_lock->unlock_fp(l->_lock))
        {
            fprintf(stderr, "cc_log_unlock(): unlock failed.\n");
        }
    }
}

static inline void cc_log_fopen(struct cc_log* l)
{
    if (0 == l->file[0] || stdout == l->fp)
    {
        l->fp = stdout;
    }
    else
    {
        l->fp = fopen(l->file, "a+");

        if (0 == l->fp)
        {
            fprintf(stderr, "cc_log_fopen(): open file %s failed, use stdout default\n", l->file);
            l->fp = stdout;
        }
    }
}

static inline void cc_log_fclose(struct cc_log*  l)
{
    if (stdout != l->fp && 0 != l->fp)
    {
        fclose(l->fp);
    }
}


static inline void cc_log_flush(struct cc_log* l)
{
    int i = 0;

    if (l->buf_inused)
    {
        cc_log_fopen(l);

        for (i = 0; i < l->buf_inused; ++i)
        {
            fprintf(l->fp, "%s", l->buffer[i]);
        }
        fflush(l->fp);

        cc_log_fclose(l);
        l->buf_inused = 0;
    }
}

struct cc_log* log_create(const char* logfile, struct lock* _lock, int delay_write)
{
    struct cc_log* l = (struct cc_log*)calloc(1, sizeof(struct cc_log));

    if (!l)
    {
        fprintf(stderr, "log_create(): calloc failed.");
        return 0;
    }

    l->delay_write = delay_write;

    /**
    if (0 != pthread_mutex_init(&l->lock, 0))
    {
        fprintf(stderr, "log_create(): mutex init failed.");
        free(l);
        return 0;
    }
    **/

    memcpy(l->file, logfile, strlen(logfile));
    l->_lock = _lock;

    return l;
}

void log_destroy(struct cc_log* l)
{
    log_flush(l);
    // pthread_mutex_destroy(&l->lock);
    free(l);
}

void log_flush(struct cc_log* l)
{
    cc_log_lock(l);
    cc_log_flush(l);
    cc_log_unlock(l);
}


void log_printf(struct cc_log* l, const char* fmt, ...)
{
    int i = 0;

    cc_log_lock(l);


    if (l->buf_inused == LOG_BUFFER_QUEUE_LEN)
    {
        cc_log_flush(l);
    }

    va_list va;
    char* buffer = NULL;

    buffer = l->buffer[l->buf_inused++];
    va_start(va, fmt);
    vsprintf(buffer, fmt, va);
    va_end(va);

    if (!l->delay_write)
    {
        cc_log_flush(l);
    }

    cc_log_unlock(l);
}


void unittest_cc_log()
{
    struct cc_log* p;
    int i = 0;
    p = log_create("123123.log", 0, 1);

    while (i < 65535)
    {
        log_printf(p, "%d\n", i++);
    }

    log_destroy(p);
}


#if 0
void log_t(const char* level, const char* file, unsigned int cur_line, char const* fmt, ...)
{
#define STR_BUFFER_SIZE 128
    char timebuf[STR_BUFFER_SIZE];
    struct timeval tv;
    time_t curtime;
    char* buffer = NULL;
    int i = 0;

    // if (run)
    {
        va_list va;

        gettimeofday(&tv, NULL);
        curtime = tv.tv_sec;
        strftime(timebuf, STR_BUFFER_SIZE, "%m-%d-%Y %T", localtime(&curtime));

        buffer = get_buffer();
        i += sprintf(buffer + i, "[%s][%s.%06lds]%s %u] ", level, timebuf, tv.tv_usec, file, cur_line);

        va_start(va, fmt);
        vsprintf(buffer + i, fmt, va);
        return_buffer();
        va_end(va);
    }
#undef STR_BUFFER_SIZE
}
#endif


